package com.example.demo.io.bio;

import java.math.BigDecimal;
import java.util.*;

/**
 *
 * @版权 : Copyright (c) 2017-2018 *********公司技术开发部
 * @author: gaozhenchao
 * @E-mail: 1226046769@qq.com
 * @版本: 1.0
 * @创建日期: 2019年1月14日 上午9:19:49
 * @ClassName CalculateUtil
 * @类描述-Description: TODO(这里用一句话描述这个方法的作用)
 * @修改记录:
 * @版本: 1.0
 */
public class Calculator {
    // 操作数栈
    private static Stack Operands;
    // 操作符栈
    private static Stack Operators;
    // 操作符集合
    private static final Set C_OPERATOR_SET = new HashSet() {
        /**
         *
         */
        private static final long serialVersionUID = 1L;
        {
            add('+');
            add('-');
            add('*');
            add('/');
            add('(');
            add(')');
        }
    };

    // 获取优先级
    private static int getOperatorPriority(char ch) {
        if (ch == '+' || ch == '-') {
            return 0;
        } else if (ch == '*' || ch == '/') {
            return 1;
        } else {
            return -1;
        }
    }

    // 中缀转后缀
    private static String infixToSuffix(String expression) throws Exception {
        Operators = new Stack<>();
        Operators.clear();
        StringBuilder sBuilder = new StringBuilder();
        for (int i = 0; i < expression.length(); i++) {
            char ch = expression.charAt(i);
            if (ch == ' ') {
                continue;
            }
            if (C_OPERATOR_SET.contains(ch)) {
                // 如果操作符栈为空
                if (Operators.empty()) {
                    if (ch == ')') { // 只要不是 ） 就入栈
                        throw new Exception("表达式错误");
                        // System.out.println("括号不匹配");
                        // return sBuilder.toString();
                    }
                    Operators.push(ch);
                } else if (ch == '(') {
                    Operators.push(ch);
                } else if (ch == ')') { // 如果是 ） 括号
                    char top;
                    while ((top = (char) Operators.peek()) != '(') { // 如果当前操作符栈的第一个不是(

                        if (Operators.empty()) {
                            // System.out.println("括号不匹配");
                            // return sBuilder.toString();
                            throw new Exception("表达式错误");
                        }
                        // 把运算符加入sBuilder
                        sBuilder.append(top);
                        Operators.pop();
                    }
                    Operators.pop();
                } else { // 如果是运算符
                    char top = (char) Operators.peek();
                    if (getOperatorPriority(ch) <= getOperatorPriority(top)) { // 如果优先级小于操作符栈中第一个
                        while (!Operators.empty()
                                && getOperatorPriority(ch) <= getOperatorPriority(top = (char) Operators.peek())) {
                            // 把运算符加入sBuilder
                            sBuilder.append(top);
                            Operators.pop();
                        }
                    }
                    Operators.push(ch);
                }
            } else { // 如果是数字 加入sBuilder（把[3]的括号去除）
                sBuilder.append("[" + ch);
                while (i + 1 < expression.length()
                        && (((ch = expression.charAt(i + 1)) == '.') || (ch >= '0' && ch <= '9'))) {
                    sBuilder.append(ch);
                    ++i;
                }
                sBuilder.append(']');
            }
        }
        while (!Operators.empty()) {
            if ((char) Operators.peek() == '(') {
                throw new Exception("表达式错误");
                // System.out.println("括号不匹配");
                // return "";
            }
            sBuilder.append(Operators.peek());
            Operators.pop();
        }
        return sBuilder.toString();
    }

    // 处理负号
    public static String handleMinus(String expression) {
        for (int i = 0; i < expression.length(); i++) {
            char ch = expression.charAt(i);
            // 存储负号个数
            int count = 0;
            // 遍历表达式，如果遍历到负号
            if (ch == '-') {
                // 存储变量加一
                count++;
                // 如果负号在第一位
                if (i == 0 && expression.charAt(i + 1) >= '0' && expression.charAt(i + 1) <= '9') {
                    // 直接在前面加0
                    expression = "0" + expression;
                    i = 0;
                    continue;
                    // 如果负号前面是括号（说明负号在表达式中） 采用的办法是把0插入到负号之前 5*(-3) -> 5*(0-3)
                } else if (expression.charAt(i - 1) == '(' && expression.charAt(i + 1) >= '0'
                        && expression.charAt(i + 1) <= '9') {
                    // 新建一个表达式长度加负号个数长度的数组（扫描一个负号添加一个0）
                    char[] c = new char[expression.length() + count];
                    // 给新的字节数组赋值
                    for (int n = 0; n < expression.length(); n++) {
                        c[n] = expression.charAt(n);

                    }
                    // 把以负号索引开始的数据右移
                    for (int j = c.length - 2; j > i - 1; j--) {
                        c[j + 1] = c[j];
                    }
                    // 赋值到i位置
                    c[i] = '0';
                    expression = String.valueOf(c);
                    // 继续向下扫描
                    i++;
                    continue;
                }

            }
        }
        return expression;

    }

    public static BigDecimal evalExp(String expression) throws Exception {
        Operands = new Stack<>();
        Operands.clear();
        // double用二进制计算，因为小数点转换成二进制运算有精度损失，而且大数据运算会转换成科学计数法，所以采用decimal
        BigDecimal ret = null;
        String suffix = infixToSuffix(expression);
        System.out.println("后缀表达式为: " + suffix);
        for (int i = 0; i < suffix.length(); i++) {
            if (suffix.charAt(i) == '[') {
                i++;
                int beginIndex = i, endIndex = i;
                while (']' != suffix.charAt(i)) {
                    i++;
                    endIndex++;
                }
                // 取两个括号中间的数字
                Operands.push(BigDecimal.valueOf(Double.valueOf(suffix.substring(beginIndex, endIndex))));
            } else {
                BigDecimal left, right;
                BigDecimal res = null;

                right = (BigDecimal) Operands.peek();
                Operands.pop();

                left = (BigDecimal) Operands.peek();
                Operands.pop();
                switch (suffix.charAt(i)) {
                    case '+':
                        res = left.add(right);

                        break;
                    case '-':
                        res = left.subtract(right);
                        break;
                    case '*':
                        res = left.multiply(right);
                        break;
                    case '/':
                        res = left.divide(right);
                        break;
                    default:
                        break;
                }
                Operands.push(res);
            }
        }
        if (Operands.size() > 1) {

            throw new Exception("表达式错误");

        }

        ret = (BigDecimal) Operands.peek();

        Operands.pop();
        return ret;
    }

    public static Integer cal(String expression) throws Exception{
        Integer result = -1;
        try {
            String operation = "";
            int index = 0;
            Iterator iterator = C_OPERATOR_SET.iterator();
            while (iterator.hasNext()) {
                String next = iterator.next().toString();
                if (expression.contains(next)){
                    operation = next;
                    index = expression.indexOf(next);
                    break;
                }
            }
            Integer first = Integer.parseInt(expression.substring(0, index));
            Integer end = Integer.parseInt(expression.substring(index + 1));
            switch (operation){
                case "+" :
                    result = first + end;
                    break;
                case "-" :
                    result = first - end;
                    break;
                case "*" :
                    result = first * end;
                    break;
                case "/" :
                    result = first / end;
                    break;
                default:
                    break;
            }
        } catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }

    // (3+4)*5 10000000*1000000 0.65656544*3
    public static void main(String[] args) throws Exception {

        Scanner input = new Scanner(System.in);
        System.out.println("请输入一个含+-*/()的表达式,请确认输入合法");
        // 5*(-3) -> 5*(0-3)
        String expression = input.nextLine();
        // 处理负号
        expression = handleMinus(expression);
        // 去除BigDecimal后面的无用0
        System.out.println(expression + " =" + evalExp(expression).stripTrailingZeros().toPlainString());
        input.close();
    }
}